#!/bin/bash

# This script dynamically creates the correct mounts based on whether
# we are running system A or B -- we will not "know" at build time
# what we are going to be booted from because the same image could
# be placed into either system A or system B root partition during
# upgrades.
#
# The dynamically changing mounts are:
# - /boot is /dev/vda4 (system A) or /dev/vda7 (system B)
# - /var is /dev/vda6 (system A) or /dev/vda9 (system B)
#
# This is generated dynamically to avoid changing /etc/fstab (root
# filesystem might be read-only).
#
# systemd doc advises to "not use shell scripts" as generators --
# while I agree with that and the rationale behind it, this script
# is written carefully and works correctly, and is quicker to write
# than setting up a compiled binary (for now).

UNIT_DIR="$1"

function get_boot_arg_value() {
    local ARGNAME="$1"
    for ARG in $(cat /proc/cmdline); do
        echo "$ARG" | while IFS='=' read KEY VALUE; do
            if [ "$ARGNAME" == "$KEY" ]; then echo "$VALUE"; fi
        done
    done
}

function make_requires() {
    mkdir -p "${UNIT_DIR}"/$1.requires
    ln -s ../$2 "${UNIT_DIR}"/$1.requires/$2
}

function make_boot_mount() {
    if [ "$1" == A ]; then
        local DEVICE=/dev/vda4
    else
        local DEVICE=/dev/vda7
    fi
    echo "# Automatically generated by /etc/systemd/system-generators/mount-generator"
    echo "[Unit]"
    echo "SourcePath=/etc/systemd/system-generators/mount-generator"
    echo "Before=local-fs.target"
    echo
    echo "[Mount]"
    echo "What=$DEVICE"
    echo "Where=/boot"
    echo "Type=ext4"
    echo "Options=defaults"
}

function make_var_cryptsetup() {
    # Check which partition to use.
    if [ "$1" == A ]; then
        local DEVICE=vda6
        local ALT_DEVICE=vda9
    else
        local DEVICE=vda9
        local ALT_DEVICE=vda6
    fi
    # Generate a unit that will set up the partition as encrypted storage.
    # The "mount" unit depends on the "blockdev@dev-mapper-var_crypt.target"
    # and will simply mount the properly prepared /dev/mapper/var_crypt.
    #
    # The unit file below is an adapted version of what the built-in systemd
    # crypttab generator creates for crypttab entries.
    echo "[Unit]"
    echo "Description=Cryptography Setup for var_crypt"
    echo "DefaultDependencies=no"
    echo "IgnoreOnIsolate=true"
    echo "After=cryptsetup-pre.target"
    echo "Before=blockdev@dev-mapper-var_crypt.target"
    echo "Wants=blockdev@dev-mapper-var_crypt.target"
    echo "Conflicts=umount.target"
    echo "Before=cryptsetup.target"
    echo "Requires=init-config.service"
    echo "After=init-config.service"
    # We need /boot/config for the decryption key (unless SEV is enabled)
    echo "RequiresMountsFor=/boot/config"
    echo "BindsTo=dev-${DEVICE}.device"
    echo "After=dev-${DEVICE}.device"
    echo "Before=umount.target"
    echo ""
    echo "[Service]"
    echo "Type=oneshot"
    echo "RemainAfterExit=yes"
    echo "TimeoutSec=0"
    echo "KeyringMode=shared"
    echo "ExecStart=/opt/ic/bin/setup-var-encryption.sh /dev/${DEVICE} /dev/${ALT_DEVICE}"
    echo "ExecStop=/lib/systemd/systemd-cryptsetup detach var_crypt"
}

CURRENT_SYSTEM=$(get_boot_arg_value dfinity.system)

make_boot_mount "$CURRENT_SYSTEM" >"$UNIT_DIR"/boot.mount
make_requires localfs.target boot.mount
make_requires boot-efi.mount boot.mount
make_requires boot-grub.mount boot.mount

make_var_cryptsetup "$CURRENT_SYSTEM" >"$UNIT_DIR"/systemd-cryptsetup@var_crypt.service
make_requires dev-mapper-var_crypt.device systemd-cryptsetup@var_crypt.service
